// tge.rs
// copyright zipxing@hotmail.com 2022~2023

//! Game包装了Model，Render
//! 以及实现了主循环等通用逻辑
//! 注意Game，Model，Render拥有统一的生命周期
//!
//! # Example
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//!    init_log(log::LevelFilter::Info, "log/tetris.log");
//!    info!("Tetris(tge.rs) start...");
//!    let ad = Audio::new();
//!    ad.play_file("assets/tetris/back.mp3", true);
//!    let mut m = TetrisModel::new();
//!    let mut r = TetrisRender::new();
//!    let mut g = Game::new(&mut m, &mut r);
//!    g.init();
//!    g.run()?;
//!    g.render.term.reset(&mut g.context);
//!    Ok(())
//! }

use crate::{GAME_FRAME, LOGO_FRAME, context::Context, event::timer_update};
use log::info;
use std::{
    any::Any,
    io,
    time::{Duration, Instant},
};

/// model接口，数据和主要逻辑接口
/// as_any是为了downcast成游戏的具体model实现
/// 为了清晰解耦，model里不能访问render
pub trait Model {
    fn init(&mut self, ctx: &mut Context);
    fn update(&mut self, ctx: &mut Context, dt: f32) {
        //头几帧用于绘制logo
        if ctx.stage <= LOGO_FRAME {
            return;
        }
        timer_update();
        self.handle_event(ctx, dt);
        self.handle_timer(ctx, dt);
        self.handle_input(ctx, dt);
        self.handle_auto(ctx, dt);
    }
    fn handle_timer(&mut self, ctx: &mut Context, dt: f32);
    fn handle_event(&mut self, ctx: &mut Context, dt: f32);
    fn handle_input(&mut self, ctx: &mut Context, dt: f32);
    fn handle_auto(&mut self, ctx: &mut Context, dt: f32);
    fn as_any(&mut self) -> &mut dyn Any;
}

/// render接口，传入context和model，进行每帧绘制
/// model要downcast成具体的GameModel才能正常使用
/// 例如：
/// let gm = model.as_any().downcast_mut::<TetrisModel>().unwrap();
pub trait Render {
    fn init(&mut self, ctx: &mut Context);
    fn update<G: Model>(&mut self, ctx: &mut Context, m: &mut G, dt: f32) {
        self.handle_event(ctx, m, dt);
        self.handle_timer(ctx, m, dt);
        self.draw(ctx, m, dt);
    }
    fn handle_event<G: Model>(&mut self, ctx: &mut Context, model: &mut G, dt: f32);
    fn handle_timer<G: Model>(&mut self, ctx: &mut Context, model: &mut G, dt: f32);
    fn draw<G: Model>(&mut self, ctx: &mut Context, model: &mut G, dt: f32);
}

/// Game包装了Model，Render和一个Context结构
pub struct Game<'gamelife, M: 'gamelife, R: 'gamelife>
where
    M: Model,
    R: Render,
{
    pub context: Context,
    pub model: &'gamelife mut M,
    pub render: &'gamelife mut R,
}

impl<'gamelife, M, R> Game<'gamelife, M, R>
where
    M: Model,
    R: Render,
{
    pub fn new(m: &'gamelife mut M, r: &'gamelife mut R) -> Self {
        info!("New game...");
        Self {
            context: Context::new(),
            model: m,
            render: r,
        }
    }

    /// 主循环，轮询键盘鼠标事件，处理定时器和注册事件
    /// 并以每秒固定帧数调用tick，执行model和render的
    /// update函数
    pub fn run(&mut self) -> io::Result<()> {
        info!("Begin run...");

        let mut last_tick = Instant::now();
        let tick_rate = Duration::from_nanos(1000_000_000 / GAME_FRAME as u64);

        loop {
            let timeout = tick_rate
                .checked_sub(last_tick.elapsed())
                .unwrap_or_else(|| Duration::from_nanos(100));

            if self
                .context
                .adapter
                .poll_event(timeout, &mut self.context.input_events)
            {
                return Ok(());
            }

            let et = last_tick.elapsed();
            if et >= tick_rate {
                let dt = et.as_secs() as f32 + et.subsec_nanos() as f32 / 1000_000_000.0;
                self.on_tick(dt);
                last_tick = Instant::now();
            }
        }
    }

    /// 每帧调用，更新定时器，更新model逻辑，更新render绘制
    fn on_tick(&mut self, dt: f32) {
        self.context.stage += 1;
        self.model.update(&mut self.context, dt);
        self.render.update(&mut self.context, self.model, dt);
    }

    /// 初始化render和model
    pub fn init(&mut self) {
        info!("Init game...");
        self.render.init(&mut self.context);
        self.model.init(&mut self.context);
    }
}
